package org.hzero.interfaces.common.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.*;

/**
 * 反射工具类
 *
 * @author tianhao.luo@hand-china.com
 */
public class KnifeReflectUtil {

    public static final Logger LOGGER = LoggerFactory.getLogger(KnifeReflectUtil.class);
    private static final String SETTER = "set";
    private static final char UNDER_LINE = '_';

    /**
     * 从当前类及父类中获取指定的方法
     *
     * @param tClass         类型
     * @param methodName     方法名
     * @param parameterTypes 方法参数
     * @param <T>            类型
     * @return 方法
     */
    public static <T> Method getMethodNotRecursion(Class<T> tClass, String methodName, Class<?>... parameterTypes) {
        Assert.isTrue(!StringUtils.isEmpty(methodName), "获取的方法名不能为空");
        Class<?> currClass = tClass;
        while (currClass != null) {
            int inputLength = parameterTypes != null ? parameterTypes.length : 0;
            Method[] declaredMethods = currClass.getDeclaredMethods();
            for (Method method : declaredMethods) {
                if (methodName.equals(method.getName())) {
                    Class<?>[] mParamTypes = method.getParameterTypes();
                    int currLength = mParamTypes.length;
                    // 参数长度不同直接跳过，并继续
                    if (currLength != inputLength) {
                        continue;
                    }
                    // 参数相同的情况：无参数 或者 有参数;无参数直接返回，有参数继续比对
                    if (parameterTypes == null) {
                        return method;
                    }
                    // 有参数比对，使用双指针
                    int l = 0, r = currLength - 1;
                    while (l <= r && l < currLength && r >= 0) {
                        String lTypeName = mParamTypes[l].getName();
                        String lCurrTypeName = parameterTypes[l].getName();
                        if (lTypeName.equals(lCurrTypeName)) {
                            l++;
                        } else {
                            break;
                        }
                        String rTypeName = mParamTypes[r].getName();
                        String rCurrTypeName = parameterTypes[r].getName();
                        if (rTypeName.equals(rCurrTypeName)) {
                            r--;
                        } else {
                            break;
                        }
                    }
                    // 产生了移动，并且走完了
                    if (l > r) {
                        return method;
                    }
                }
            }
            // 还是没有,去父类中找
            currClass = currClass.getSuperclass();
        }
        return null;
    }


    /**
     * 提供给从Mapper中获取接口方法
     * <p>
     * 1。标签的id属性值在任何时候都不能出现英文句号“.”,并且同一个命名空间下不能出
     * 现重复的id
     * 2。因为接口方法是可以重载的,所以接口中可以出现多个同名但参数不同的方法,但是
     * XML中id的值不能重复,因而接口中的所有同名方法会对应着XML中的同一个id
     * 的方法。最常见的用法就是,同名方法中其中一个方法增加一个 ROWBound类型的参
     * 数用于实现分页查询
     * 3。此处默认同一个id只有一个方法
     *
     * @param tClass     类型
     * @param methodName 方法名
     * @param <T>        类型
     * @return 方法
     */
    public static <T> Method getMethodNotRecursion(Class<T> tClass, String methodName) {
        Assert.isTrue(!StringUtils.isEmpty(methodName), "获取的方法名不能为空");
        Class<?> currClass = tClass;
        while (currClass != null) {
            Method[] declaredMethods = currClass.getDeclaredMethods();
            int length = declaredMethods.length;
            int l = 0, r = length - 1;
            while (l <= r) {
                Method leftMethod = declaredMethods[l];
                Method rightMethod = declaredMethods[r];
                if (methodName.equals(leftMethod.getName())) {
                    return leftMethod;
                }
                if (methodName.equals(rightMethod.getName())) {
                    return rightMethod;
                }
                l++;
                r--;
            }
            // 还是没有,去父类中找
            currClass = currClass.getSuperclass();
        }
        return null;
    }


    /**
     * 专门用于JdbcTemplate查询数据返回的List类型的map，转化为tClass对象
     *
     * @param tClass  class类
     * @param mapList jdbcTemplate执行查询时返回多条结果封装的结果
     * @param <T>     范型
     * @return 将数据转化为List
     */
    public static <T> List<T> covertListMap(Class<T> tClass, List<Map<String, Object>> mapList) {
        ArrayList<T> arrayList = new ArrayList<>();
        for (Map<String, Object> map : mapList) {
            T obj = covertMap(tClass, map);
            arrayList.add(obj);
        }
        return arrayList;
    }


    /**
     * 专门用于JdbcTemplate查询数据返回的map，转化为tClass对象
     *
     * @param tClass class类
     * @param map    jdbcTemplate执行查询时封装单条记录的结果
     * @param <T>    占位符
     * @return 范型对象
     */
    public static <T> T covertMap(Class<T> tClass, Map<String, Object> map) {
        T obj = null;
        try {
            obj = tClass.newInstance();
            Method[] methods = tClass.getMethods();
            // 过滤一遍
            HashMap<String, Method> setterMethodMap = new HashMap<>(12);
            for (Method item : methods) {
                if (item.getName().startsWith(SETTER)) {
                    setterMethodMap.put(item.getName(), item);
                }
            }
            // 利用所有的set方法进行设置值
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String key = entry.getKey();
                String setterMethodName = getSetterMethodNameOfColumnName(key);
                Method method = setterMethodMap.get(setterMethodName);
                if (!method.isAccessible()) {
                    method.setAccessible(true);
                }
                Object value = entry.getValue();
                if (value != null) {
                    Object tempValue = value;
                    // 调用之前需要判断参数类型，并进行转化
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    // setter方法只有一个属性
                    Class<?> parameterType = parameterTypes[0];
                    Class<?> valueClass = value.getClass();
                    // 将map中的值类型转化为setter方法需要的类型
                    if (valueClass.equals(BigDecimal.class) && parameterType.equals(Long.class)) {
                        tempValue = ((BigDecimal) value).longValue();
                    } else if (valueClass.equals(BigDecimal.class) && parameterType.equals(Integer.class)) {
                        tempValue = ((BigDecimal) value).intValue();
                    } else if (valueClass.equals(BigDecimal.class) && parameterType.equals(Float.class)) {
                        tempValue = ((BigDecimal) value).floatValue();
                    } else if (valueClass.equals(BigDecimal.class) && parameterType.equals(Double.class)) {
                        tempValue = ((BigDecimal) value).doubleValue();
                    } else if (valueClass.equals(BigDecimal.class) && parameterType.equals(String.class)) {
                        tempValue = ((BigDecimal) value).toPlainString();
                    } else if (valueClass.equals(Timestamp.class) && parameterType.equals(Date.class)) {
                        tempValue = Date.from(((Timestamp) value).toInstant());
                    } else if (valueClass.equals(Boolean.class) && parameterType.equals(Boolean.class)) {
                        tempValue = value;
                    }
                    method.invoke(obj, tempValue);
                }
            }
        } catch (Exception e) {
            LOGGER.error("反射通过setter方法设置属性失败:{}", e.getMessage());
        }
        return obj;
    }

    /**
     * 获取下划线命名属性名的setter方法
     *
     * @param columnName 下划线类型属性名
     * @return setter方法名称
     */
    private static String getSetterMethodNameOfColumnName(String columnName) {
        StringBuilder sb = new StringBuilder(SETTER);
        // 两种情况，
        // 1 没有下划线，则不需要驼峰转化
        // 2 有下划线，驼峰转化
        boolean nextUpperCase = false;
        for (int i = 0; i < columnName.length(); i++) {
            char c = columnName.charAt(i);
            // 如果是下划线并且已保存的字符长度大于0，
            // 表示下一个字符需要转化为大写
            if (c == '_' && sb.length() > 0) {
                nextUpperCase = true;
            } else {
                if (nextUpperCase) {
                    sb.append(Character.toUpperCase(c));
                    nextUpperCase = false;
                } else {
                    if (i == 0) {
                        sb.append(Character.toUpperCase(c));
                    } else {
                        sb.append(Character.toLowerCase(c));
                    }
                }
            }
        }
        return sb.toString();
    }

    /**
     * 获取类中属性名的setter方法
     *
     * @param fieldName 属性名
     * @return setter方法名
     */
    private static String getSetterMethodNameOfFieldName(String fieldName) {
        StringBuilder sb = new StringBuilder(SETTER);
        // 将类中的属性名转化为setter类型
        for (int i = 0; i < fieldName.length(); i++) {
            if (i == 0) {
                sb.append(Character.toUpperCase(fieldName.charAt(i)));
            } else {
                sb.append(fieldName.charAt(i));
            }
        }
        return sb.toString();
    }


    /**
     * 下划线转驼峰
     *
     * @param inputString 输入的带下划线的字段
     * @return 驼峰命名
     */
    private static String underlineToCamelHump(String inputString) {
        StringBuilder sb = new StringBuilder();
        boolean nextUpperCase = false;
        for (int i = 0; i < inputString.length(); i++) {
            char c = inputString.charAt(i);
            // 如果是下划线并且已保存的字符长度大于0，
            // 表示下一个字符需要转化为大写
            if (c == '_' && sb.length() > 0) {
                nextUpperCase = true;
            } else {
                if (nextUpperCase) {
                    sb.append(Character.toUpperCase(c));
                    nextUpperCase = false;
                } else {
                    sb.append(Character.toLowerCase(c));
                }
            }
        }
        return sb.toString();
    }


    /**
     * 扫描所有方法
     *
     * @param tClass class类型
     * @param <T>    占位符
     * @return 所有方法
     */
    public static <T> List<Method> getAllMethodNotRecursion(Class<T> tClass) {
        List<Method> allMethods = new ArrayList<>();
        Class<?> currClass = tClass;
        while (currClass != null) {
            Method[] declaredMethods = currClass.getDeclaredMethods();
            allMethods.addAll(Arrays.asList(declaredMethods));
            currClass = tClass.getSuperclass();
        }
        return allMethods;
    }

    /**
     * 扫描指定类上所有加了{@link Annotation}注解的属性
     *
     * @param scanClass 待扫描类
     * @param <T>       泛型
     * @return 扫描出的属性
     */
    public static <T,A extends Annotation> List<Field> getAllFieldRecursionWithAnnotation(Class<T> scanClass, Class<A> annotationClass) {
        // 1. 扫描当前类及父类所有加了指定注解的属性
        List<Field> orderFieldList = new ArrayList<>();

        Field[] classFieldArray = scanClass.getDeclaredFields();
        int count = 0;
        while (count < classFieldArray.length) {
            Field temp = classFieldArray[count];
            A annotation = temp.getAnnotation(annotationClass);
            if (annotation != null) {
                orderFieldList.add(temp);
            }
            count++;
        }

        // 继续扫描父类
        Class<? super T> superclass = scanClass.getSuperclass();
        while (superclass != null) {
            List<Field> supperClassFieldList = getAllFieldRecursionWithAnnotation(superclass,annotationClass);
            orderFieldList.addAll(supperClassFieldList);
            superclass = superclass.getSuperclass();
        }

        // 返回所有的属性
        return orderFieldList;
    }

}
